package cc.shacocloud.mirage.restful;

import cc.shacocloud.mirage.restful.bind.WebDataBinderFactory;
import cc.shacocloud.mirage.utils.FutureUtils;
import cc.shacocloud.mirage.utils.MethodParameter;
import cc.shacocloud.mirage.utils.collection.ArrayUtil;
import cc.shacocloud.mirage.utils.reflection.DefaultParameterNameDiscoverer;
import cc.shacocloud.mirage.utils.reflection.ParameterNameDiscoverer;
import io.vertx.core.Future;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 基于{@link HandlerMethod}的扩展
 * <p>
 * 通过 {@link HandleMethodArgumentResolver} 列表支持当前请求解析的处理来处理解析
 */
@Slf4j
public class InvocableHandlerMethod extends HandlerMethod {
    
    private static final Future<Object[]> EMPTY_ARGS = Future.succeededFuture(new Object[0]);
    
    /**
     * 参数名称发现器
     */
    @Nullable
    protected ParameterNameDiscoverer parameterNameDiscoverer;
    
    /**
     * 处理方法参数解析器
     */
    @Nullable
    protected HandleMethodArgumentResolverComposite resolvers;
    
    public InvocableHandlerMethod(HandlerMethod handlerMethod) {
        super(handlerMethod);
    }
    
    public InvocableHandlerMethod(Object bean, Method method) {
        super(bean, method);
    }
    
    public InvocableHandlerMethod(Object bean, String methodName, Class<?>... parameterTypes)
            throws NoSuchMethodException {
        super(bean, methodName, parameterTypes);
    }
    
    
    /**
     * 设置 {@link HandleMethodArgumentResolverComposite} 以用于解析方法参数值
     */
    public void setHandlerMethodArgumentResolvers(HandleMethodArgumentResolverComposite argumentResolvers) {
        this.resolvers = argumentResolvers;
    }
    
    /**
     * 设置 {@code ParameterNameDiscoverer} 以在需要时解析参数名称（例如默认请求属性名称）。
     * <p>
     * 默认为{@link DefaultParameterNameDiscoverer}
     */
    public void setParameterNameDiscoverer(@Nullable ParameterNameDiscoverer parameterNameDiscoverer) {
        this.parameterNameDiscoverer = parameterNameDiscoverer;
    }
    
    
    public Future<?> invokeForRequest(HttpRequest request, WebDataBinderFactory binderFactory, Object... providedArgs) throws Exception {
        if (log.isDebugEnabled()) {
            log.debug("方法 '" + this + "' 进入参数解析过程...");
        }
        
        return getMethodArgumentValues(request, binderFactory, providedArgs)
                .compose(args -> {
                    if (log.isDebugEnabled()) {
                        log.debug("\n执行目标方法：" + getMethod() + "\n参数：" + Arrays.toString(args));
                    }
                    
                    try {
                        return (Future<?>) doInvoke(args);
                    } catch (Exception e) {
                        return Future.failedFuture(e);
                    }
                });
    }
    
    /**
     * 获取当前请求的方法参数值，检查提供的参数值并返回配置的参数解析器。
     * <p>
     * 结果数组将传递到{@link #doInvoke}中
     */
    protected Future<Object[]> getMethodArgumentValues(HttpRequest request,
                                                       WebDataBinderFactory binderFactory,
                                                       @Nullable Object... providedArgs) throws Exception {
        MethodParameter[] parameters = getMethodParameters();
        if (ArrayUtil.isEmpty(parameters)) return EMPTY_ARGS;
        
        if (this.parameterNameDiscoverer == null) {
            throw new IllegalArgumentException("未设置 ParameterNameDiscoverer！");
        }
        if (this.resolvers == null) {
            throw new IllegalArgumentException("未设置 HandleMethodArgumentResolverComposite！");
        }
        
        final Object[] args = new Object[parameters.length];
        final AtomicInteger index = new AtomicInteger(0);
        
        return FutureUtils.sequential(parameters, (parameter, t) -> {
                    parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
                    
                    // 根据参数类型，尝试填充提供的参数，
                    Object providedArgument = findProvidedArgument(parameter, providedArgs);
                    if (providedArgument != null) {
                        args[index.getAndIncrement()] = providedArgument;
                        return Future.succeededFuture();
                    }
                    
                    if (!this.resolvers.supportsParameter(parameter)) {
                        return Future.failedFuture(new IllegalStateException(formatArgumentError(parameter, "未找到支持的参数解析器！")));
                    }
                    
                    return this.resolvers.resolveArgument(request, parameter, binderFactory)
                            .onSuccess(arg -> args[index.getAndIncrement()] = arg)
                            .onFailure(ex -> {
                                // 将堆栈跟踪留给以后使用，实际上可以解决和处理异常...
                                if (log.isDebugEnabled()) {
                                    String exMsg = ex.getMessage();
                                    if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
                                        log.debug(formatArgumentError(parameter, exMsg));
                                    }
                                }
                            });
                })
                .compose(r -> Future.succeededFuture(args));
    }
    
    /**
     * 使用给定的参数值调用处理程序方法
     */
    protected Object doInvoke(Object... args) throws Exception {
        final Method method = getMethod();
        final Object bean = getBean();
        
        // 方法设置为可访问
        if ((!Modifier.isPublic(method.getModifiers()) ||
                !Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !method.canAccess(bean)) {
            method.setAccessible(true);
        }
        
        try {
            return method.invoke(bean, args);
        } catch (IllegalArgumentException ex) {
            assertTargetBean(method, bean, args);
            throw new IllegalStateException(formatInvokeError("非法参数异常：", args), ex);
        } catch (InvocationTargetException ex) {
            // 展开 HandlerExceptionResolvers ...
            Throwable targetException = ex.getTargetException();
            if (targetException instanceof RuntimeException) {
                throw (RuntimeException) targetException;
            } else if (targetException instanceof Error) {
                throw (Error) targetException;
            } else if (targetException instanceof Exception) {
                throw (Exception) targetException;
            } else {
                throw new IllegalStateException(formatInvokeError("目标对象方法调用失败异常：", args), targetException);
            }
        }
    }
    
}
